package util

import (
	"encoding/base64"
	"fmt"
	"strconv"
	"strings"

	// "github.com/volatiletech/sqlboiler/v4/queries/qm"
)

type CursorType string

const (
	CursorTypeOffset CursorType = "OFFSET"
	CursorTypeCursor CursorType = "CURSOR" // 单方向Cursor
)

func GetIDFromCursor(id string) interface{} { //// IDSeparator 定义在 convert.go
	splitID := strings.SplitN(id, IDSeparator, 2)
	if len(splitID) != 2 {
		return 0
	}

	return splitID[1]
}

func GetEntityNameFromCursor(id string) string { ////
	splitID := strings.SplitN(id, IDSeparator, 2)
	if len(splitID) != 2 {
		return ""
	}

	return splitID[0]
}

func ZeroOrMore(limit int) int { ////
	if limit < 0 {
		return 0
	}

	return limit
}

type ComparisonSign string

const (
	ComparisonSignBiggerThan         ComparisonSign = ">"
	ComparisonSignBiggerThanOrEqual  ComparisonSign = ">="
	ComparisonSignSmallerThan        ComparisonSign = "<"
	ComparisonSignSmallerThanOrEqual ComparisonSign = "<="
)

func GetComparison(
	pagination *ConnectionPagination,
	reverse bool,
	direction SortDirection,
) ComparisonSign { ////
	if pagination != nil {
		if reverse {
			if pagination.Backward != nil {
				if direction == SortDirectionDesc {
					return getBackwardComparisonDesc()
				}

				return getBackwardComparisonAsc()
			}
		} else {
			if pagination.Forward != nil {
				if direction == SortDirectionDesc {
					return getForwardComparisonDesc()
				}

				return getForwardComparisonAsc()
			}
		}
	}

	return ""
}

func getForwardComparisonAsc() ComparisonSign { ////
	return ComparisonSignBiggerThan
}

func getForwardComparisonDesc() ComparisonSign { ////
	return ComparisonSignSmallerThan
}

func getBackwardComparisonDesc() ComparisonSign { ////
	return ComparisonSignBiggerThan
}

func getBackwardComparisonAsc() ComparisonSign { ////
	return ComparisonSignSmallerThan
}

func GetCursor(pagination *ConnectionPagination) *string { ////
	if pagination != nil {
		if pagination.Forward != nil {
			return pagination.Forward.After
		}

		if pagination.Backward != nil {
			return pagination.Backward.Before
		}
	}

	return nil
}

func GetLimit(pagination *ConnectionPagination) int { ////
	var limit int

	if pagination == nil {
		return limit
	}

	if pagination.Forward != nil && pagination.Forward.First != nil {
		limit = ZeroOrMore(*pagination.Forward.First)
	}

	if limit == 0 && pagination.Backward != nil && pagination.Backward.Last != nil {
		limit = ZeroOrMore(*pagination.Backward.Last)
	}

	if pagination.Default != nil {
		if limit == 0 {
			limit = ZeroOrMore(pagination.Default.PageSize)
		}
		if limit > pagination.Default.MaxSize {
			limit = ZeroOrMore(pagination.Default.MaxSize)
		}
	}

	return limit
}

func GetPageInfo(pagination *ConnectionPagination) (int32, *string, bool) { ////
	var limit int
	var cursor *string
	var reverse bool

	if pagination != nil {
		if pagination.Forward != nil && pagination.Forward.First != nil {
			limit = ZeroOrMore(*pagination.Forward.First)
			cursor = pagination.Forward.After
		}

		if limit == 0 && pagination.Backward != nil && pagination.Backward.Last != nil {
			limit = ZeroOrMore(*pagination.Backward.Last)
			cursor = pagination.Backward.Before
			reverse = true
		}

		if pagination.Default != nil {
			if limit == 0 {
				limit = ZeroOrMore(pagination.Default.PageSize)
			}
			if limit > pagination.Default.MaxSize {
				limit = ZeroOrMore(pagination.Default.MaxSize)
			}
		}
	}

	return int32(limit), cursor, reverse
}

func GetOffsetFromCursor(cursor *string) int32 { ////
	if cursor == nil {
		return 0
	}

	i, err := strconv.Atoi(*cursor)
	if err != nil {
		return 0
	}

	return int32(i)
}

// func FromOffsetCursor(cursor string) int {
// 	offset, err := strconv.Atoi(cursor)
// 	if err != nil {
// 		return nil
// 	}

// 	if offset > 0 {
// 		return []qm.QueryMod{
// 			qm.Offset(offset),
// 		}
// 	}

// 	return nil
// }

func GetDirection(direction SortDirection, reverse bool) SortDirection { //// 根据reverse切换排序方向
	if reverse {
		if direction == SortDirectionAsc {
			return SortDirectionDesc
		}

		return SortDirectionAsc
	}

	return direction
}

func GetOrderBy(dbColumn string, direction SortDirection) string { //// 组合 orderby的field和order为字符串
	return dbColumn + " " + string(direction)
}

func CursorTypeCounter() (func(SortDirection), func() CursorType) { ////
	var asc, desc int

	return func(d SortDirection) {
			switch d {
			case SortDirectionDesc:
				desc++
			case SortDirectionAsc:
				asc++
			}
		}, func() CursorType {
			oneDirectionOnly := asc == 0 || desc == 0
			if oneDirectionOnly {
				return CursorTypeCursor
			}

			return CursorTypeOffset
		}
}

const (
	cursorValueSeparator = ":"
	cursorSliceSeparator = "--##%$(_)$%##--"
)

func ToCursorValue(k string, v interface{}) string { //// 键值对组合为游标数组的一项
	return fmt.Sprintf("%s%s%v", k, cursorValueSeparator, v)
}

func FromCursorValue(cursor string) (string, string) { //// 切割游标数组的每一项为键值对
	keyValuePair := strings.SplitN(cursor, cursorValueSeparator, 2)
	if len(keyValuePair) != 2 {
		return "", ""
	}

	return keyValuePair[0], keyValuePair[1]
}

func StringToInterface(v string) interface{} { ////
	if v == "" {
		return nil
	}

	return v
}

func CursorValuesToString(v []string) string {
	return base64.StdEncoding.EncodeToString([]byte(strings.Join(v, cursorSliceSeparator)))
}

func CursorStringToValues(v string) []string { //// 复杂游标解码、切割为数组
	bytes, err := base64.StdEncoding.DecodeString(v)
	if err != nil {
		return nil
	}

	return strings.Split(string(bytes), cursorSliceSeparator)
}

func ToOffsetCursor(index int) *string { ////
	str := strconv.Itoa(index + 1)
	return &str
}

func parenthese(v string) string { ////
	return "(" + v + ")"
}

func GetCursorWhere(comparisonSign ComparisonSign, columns []string, values []interface{}) string { //// 返回 (c1, c2) < (?,?)
	return parenthese(strings.Join(columns, ", ")) + " " +
		string(comparisonSign) + " " +
		parenthese(strings.TrimSuffix(strings.Repeat("?,", len(values)), ","))
}

func BaseConnection(
	length int,
	reverse bool,
	appendEdge func(i int),
) { //// 组装edges（reverse意味着反向组装）
	if reverse {
		// reverse the order of the results
		for i := length - 1; i >= 0; i-- {
			appendEdge(i)
		}
	} else {
		for i := 0; i < length; i++ {
			appendEdge(i)
		}
	}
}

func NewBackwardPagination(last *int, before *string) *ConnectionPagination { ////
	return &ConnectionPagination{
		Backward: &ConnectionBackwardPagination{
			Last:   last,
			Before: before,
		},
	}
}

func NewForwardPagination(first *int, after *string) *ConnectionPagination { ////
	return &ConnectionPagination{
		Forward: &ConnectionForwardPagination{
			First: first,
			After: after,
		},
	}
}

func NewPagination(
	first *int,
	last *int,
	after *string,
	before *string,
	defaultPageSize int,
	maxSize int,
) *ConnectionPagination { ////
	return &ConnectionPagination{
		Backward: &ConnectionBackwardPagination{
			Last:   last,
			Before: before,
		},
		Forward: &ConnectionForwardPagination{
			First: first,
			After: after,
		},
		Default: &DefaultPagination{
			PageSize: defaultPageSize,
			MaxSize:  maxSize,
		},
	}
}
